package ldh.maker.freemaker;

import ldh.bean.util.ValidateHelp;
import ldh.common.Pageable;
import ldh.database.Column;
import ldh.database.Table;
import ldh.maker.database.TableInfo;
import ldh.maker.util.FreeMakerUtil;
import org.apache.log4j.Logger;

import java.util.*;
import java.util.Map.Entry;

public class MakerInfo {

	//必须要有
	private String xmlPath; // xml输出路径
	private String srcPath;
	private String pojoPackage; 
	private String pojoWherePackage;
	private String daoPackage; 
	private String servicePackage; 
	private String serviceImplPackage; 
	private String controllerPackage; 
	private Set<String> excludeTables  = new HashSet<String>();
	
	//提供了默认值
	private String daoSuffix = "Dao";
	private String pojoWhereSuffix = "Where";
	private String serviceSuffix = "Service";
	private String serviceImplSuffix = "ServiceImpl";
	private String controllerSuffix = "Controller";
	
	private Class<?> extendsClass;
	private String extendsName;
	
	//可有可无
	private String mybatisConfigPath;
	
	private String pojoPath;
	private String pojoWherePath;
	private String daoPath;
	private String servicePath;
	private String serviceImplPath;
	private String controllerPath;
	private String jspPath;
	private String jsPath;
	private List<String> jspFtls = new ArrayList<String>();
	private List<String> jsFtls = new ArrayList<String>();
	private Map<String, String> typeHandlers;
	private TableInfo tableInfo;
	
	private static final Logger logger = Logger.getLogger(MakerInfo.class);
	
	public MakerInfo extend(Class<?> extendsClass, String extendsName) {
		this.extendsName = extendsName;
		this.extendsClass = extendsClass;
		return this;
	}
	
	public MakerInfo exculdeTable(String... tableNames) {
		if (tableNames != null && tableNames.length > 0) {
			for (String tn : tableNames) {
				excludeTables.add(tn);
			}
		}
		return this;
	}
	
	public MakerInfo xmlPath(String xmlPath) {
		this.xmlPath = xmlPath;
		return this;
	}
	
	public MakerInfo jspFtl(String... jspFtl) {
		if (jspFtl != null) {
			for (String jf : jspFtl) {
				jspFtls.add(jf);
			}
		}
		return this;
	}
	
	public MakerInfo jsFtl(String... jsFtl) {
		if (jsFtl != null) {
			for (String jf : jsFtl) {
				jsFtls.add(jf);
			}
		}
		return this;
	}
	
	public MakerInfo jspPath(String jspPath) {
		this.jspPath = jspPath;
		return this;
	}
	
	public MakerInfo jsPath(String jsPath) {
		this.jsPath = jsPath;
		return this;
	}
	
	public MakerInfo srcPath(String srcPath) {
		this.srcPath = srcPath;
		return this;
	}
	
	public MakerInfo pojoPackage(String pojoPackage) {
		this.pojoPackage = pojoPackage;
		return this;
	}
	
	public MakerInfo pojoWherePackage(String pojoWherePackage) {
		this.pojoWherePackage = pojoWherePackage;
		return this;
	}
	
	public MakerInfo daoPackage(String daoPackage) {
		this.daoPackage = daoPackage;
		return this;
	}
	
	public MakerInfo servicePackage(String servicePackage) {
		this.servicePackage = servicePackage;
		return this;
	}
	
	public MakerInfo serviceImplPackage(String serviceImplPackage) {
		this.serviceImplPackage = serviceImplPackage;
		return this;
	}
	
	public MakerInfo controllerPackage(String controllerPackage) {
		this.controllerPackage = controllerPackage;
		return this;
	}
	
	public MakerInfo daoSuffix(String daoSuffix) {
		this.daoSuffix = daoSuffix;
		return this;
	}
	
	public MakerInfo pojoWhereSuffix(String pojoWhereSuffix) {
		this.pojoWhereSuffix = pojoWhereSuffix;
		return this;
	}
	
	public MakerInfo tableInfo(TableInfo tableInfo) {
		this.tableInfo = tableInfo;
		return this;
	}
	
	public MakerInfo mybatisConfigPath(String mybatisConfigPath) {
		this.mybatisConfigPath = mybatisConfigPath;
		return this;
	}
	
	private void check() {
		ValidateHelp vh = new ValidateHelp()
			.notEmpty(xmlPath)
			.notEmpty(srcPath)
			.notEmpty(jspPath)
			.notEmpty(jsPath)
			.notEmpty(pojoPackage)
			.notEmpty(pojoWherePackage)
			.notEmpty(daoPackage)
			.notEmpty(controllerPackage)
			.notNull(tableInfo)
			;
		if (vh.hasErrors()) {
			vh.errors();
		}
	}
	
	private void create() {
		String sp = System.getProperty("file.separator");
		String pp = pojoPackage.replace(".", sp);
		
		pojoPath = path(pojoPackage);
		pojoWherePath = path(pojoWherePackage);
		daoPath = path(daoPackage);
		servicePath = path(servicePackage);
		serviceImplPath = path(serviceImplPackage);
		controllerPath = path(controllerPackage);
		
		if (isEmpty(xmlPath) && (xmlPath.endsWith("/") || xmlPath.endsWith("\\"))) {
			xmlPath = xmlPath  + pp;
		} else {
			xmlPath = xmlPath + sp + pp;
		}
		
		if (isEmpty(mybatisConfigPath)) {
			typeHandlers = MybatisConfigReader.getTypeHandlers(mybatisConfigPath);
		}
	}
	
	private String path(String pack) {
		String sp = System.getProperty("file.separator");
		String pp = pack.replace(".", sp);
		String str;
		if (srcPath != null && (srcPath.endsWith("/") || srcPath.endsWith("\\"))) {
			str = srcPath  + pp;
		} else {
			str = srcPath + sp + pp;
		}
		return str;
	}
	
	private boolean isExculde(String tableName) {
		for (String tn : this.excludeTables) {
			if (tn.equalsIgnoreCase(tableName)) {
				return true;
			}
		}
		return false;
	}
	
	public String make() {
		check();
		create();
		
		List<BeanMaker<?>> beanMakers = new ArrayList<BeanMaker<?>>();
		List<DaoMaker> daoMakers = new ArrayList<DaoMaker>();
		
//		new JspMainMaker()
//			.tableInfo(tableInfo)
//			.outPath(jspPath)
//			.make();
		
		for (Entry<String, Table> entry : tableInfo.getTables().entrySet()) {
			Table table = entry.getValue();
			
			if (!table.isCreate()) continue;
			if (isExculde(table.getName())) continue;
			String beanName = FreeMakerUtil.firstUpper(table.getJavaName());
			
			Class<?> key = null;
			if (table.getPrimaryKey() != null && !table.getPrimaryKey().isComposite()) {
				Column c = table.getPrimaryKey().getColumns().iterator().next();
				key = c.getPropertyClass();
			}
//			if (key == null) {
//				logger.error(table.getName() + " key 为复合主键或为无主键，需要特殊处理！！");
//				continue;
//				//throw new RuntimeException(table.getName() + " key 为复合主键，需要特殊处理！！");
//			}
			
			KeyMaker keyMaker = null;
			if (table.getPrimaryKey().isComposite()) {
				KeyMaker keyM = new KeyMaker()
				 	.pack(pojoPackage)
				 	.extend(extendsClass, extendsName)
				 	.primaryKey(table.getPrimaryKey())
				 	.outPath(pojoPath)
				 	.make();
				keyMaker = keyM;
				
				table.setCreate(false);
			}
			
			PojoMaker bm = new PojoMaker()
			 	.pack(pojoPackage)
			 	.extend(extendsClass, extendsName)
			 	.table(table)
			 	.key(key, keyMaker)
			 	.outPath(pojoPath)
			 	.make();
			
			PojoWhereMaker pm = new PojoWhereMaker()
			 	.pack(pojoWherePackage)
			 	.outPath(pojoWherePath)
			 	.className(beanName + pojoWhereSuffix)
			 	.extend(bm)
			 	.table(table)
			 	.key(key, keyMaker)
			 	.implement(Pageable.class)
			 	.serializable(bm.isSerializable())
			 	.make();
			
			DaoMaker daoMaker = new DaoMaker()
			 	.pack(daoPackage)
			 	.outPath(daoPath)
			 	.key(key, keyMaker)
			 	.className(beanName + daoSuffix)
			 	.beanMaker(bm)
			 	.imports(pm)
			 	.table(table);
			
			new MapperMaker()
				.namespace(daoMaker.getName())
				.table(table)
				.typeHandlers(typeHandlers)
				.outPath(xmlPath)
				.key(key, keyMaker)
				.make();
			
			if (keyMaker != null) continue;
			beanMakers.add(bm);
			
			
			beanMakers.add(pm);
			daoMaker.make();
			
			daoMakers.add(daoMaker);
			
//			ServiceInterfaceMaker sm = new ServiceInterfaceMaker()
//			 	.pack(servicePackage)
//			 	.outPath(servicePath)
//			 	.key(key, keyMaker)
//			 	.className(beanName + serviceSuffix)
//			 	.beanWhereMaker(bm)
//			 	.imports(pm)
//			 	.table(table)
//				.make();
//			
//			new ServiceImplMaker()
//			 	.pack(serviceImplPackage)
//			 	.outPath(serviceImplPath)
//			 	.key(key, keyMaker)
//			 	.className(beanName + serviceImplSuffix)
//			 	.beanWhereMaker(bm)
//			 	.daoMaker(daoMaker)
//			 	.extend(sm)
//			 	.imports(pm)
//			 	.table(table)
//				.make();
			
			ServiceMaker sm = new ServiceMaker()
			 	.pack(servicePackage)
			 	.outPath(servicePath)
			 	.key(key, keyMaker)
			 	.className(beanName + serviceSuffix)
			 	.beanWhereMaker(bm)
			 	.daoMaker(daoMaker)
			 	.imports(pm)
			 	.table(table)
				.make();
			
//			new ControllerMaker()
//			 	.pack(controllerPackage)
//			 	.outPath(controllerPath)
//			 	.key(key, keyMaker)
//			 	.className(beanName + controllerSuffix)
//			 	.beanMaker(bm)
//			 	.beanWhereMaker(pm)
//			 	.service(sm)
//			 	.imports(pm)
//			 	.table(table)
//				.make();
		
			
			
//			if (jspFtls != null) {
//				for (String jspFtl : jspFtls) {
//					new JspMaker()
//						.table(table)
//						.outPath(jspPath)
//						.ftl(jspFtl)
//						.make();
//				}
//			}
			
//			if (jsFtls != null) {
//				for (String jsFtl : jsFtls) {
//					new JsMaker()
//					 	.table(table)
//					 	.outPath(jsPath)
//					 	.ftl(jsFtl)
//					 	.make();
//				}
//			}
			
			
		}
		
		String ss = print(beanMakers);
		String daoes = printDaoes(daoMakers);
		ss += "\r\n" + daoes;
		System.out.println();
		System.out.println(daoes);
		
		return ss;
	}
	
	private String print(List<BeanMaker<?>> bms) {
		StringBuilder sb = new StringBuilder();
		
		StringBuilder mappers = new StringBuilder();
		for (BeanMaker<?> bm : bms) {
			String tt = "<typeAlias alias=\"" + bm.getSimpleName() +"\" type=\"" + bm.getName() + "\"/>";
			System.out.println(tt);
			sb.append(tt).append("\r\n");
			
			if (!bm.getSimpleName().contains("Where")) {
				String path = bm.getName();
				path = path.replace(".", "/");
				mappers.append("<mapper resource=\"" + path + ".xml\"/>").append("\r\n");
			}
			
			
		}
		return sb.toString() + "\r\n" + mappers.toString() + "\r\n";
	}
	
	private String printDaoes(List<DaoMaker> bms) {
		StringBuilder sb = new StringBuilder();
		for (DaoMaker bm : bms) {
			String tt = "<bean id=\"" + FreeMakerUtil.firstLower(bm.className) +"\" class=\"org.mybatis.spring.mapper.MapperFactoryBean\">" + 
					"<property name=\"mapperInterface\" value=\"" + bm.getName() + "\" />" + 
					"<property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\" /> " +
					"</bean>";
			sb.append(tt).append("\r\n");
		}
		return sb.toString() ;
	}
	
	private boolean isEmpty(String str) {
		if (str != null && !str.equals("")) {
			return false;
		}
		
		return true;
	}
	
}
